package com.java.base.io.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.nio.charset.StandardCharsets;


public class NettyServer {


    static int port = 9000;


    public void start() {
        EventLoopGroup masterGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(1);


        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(masterGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024);

            NettyServerChannelHandler channelHandler = new NettyServerChannelHandler();

            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
//                    ch.pipeline().addLast("decoder", new StringDecoder())
//                            .addLast("encoder", new StringEncoder())
//                    ch.pipeline().addLast(new NettyServerChannelHandler());
                    ch.pipeline().addLast(channelHandler);
                }
            });

            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            channelHandler.serverChannel = channelFuture.channel();

            System.out.println("Server run on " + port);

//
//            logger.info(Integer.toString(System.identityHashCode(channelFuture)));
//            channelFuture.addListener((ChannelFutureListener) future -> {
//                logger.info(Integer.toString(System.identityHashCode(future)));
//            });

            channelFuture.channel().closeFuture().sync();
            System.out.println("Server stop");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            masterGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

    @ChannelHandler.Sharable
    public static class NettyServerChannelHandler extends ChannelInboundHandlerAdapter {

        private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
         Channel serverChannel;


        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            String msg = MsgRender.msg(Thread.currentThread().getName() + "系统消息", ctx.channel().remoteAddress() + " 上线，当前人数 " + (channelGroup.size() + 1));
            channelGroup.writeAndFlush(Unpooled.copiedBuffer(msg.getBytes(StandardCharsets.UTF_8)));
            channelGroup.add(ctx.channel());
            System.out.println(msg);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            channelGroup.remove(ctx.channel());
            String msg = MsgRender.msg(Thread.currentThread().getName() + "系统消息", ctx.channel().remoteAddress() + " 下线，当前人数 " + channelGroup.size());
            channelGroup.writeAndFlush(Unpooled.copiedBuffer(msg.getBytes(StandardCharsets.UTF_8)));
            System.out.println(msg);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();
            cause.printStackTrace();
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            String clientMsg = ((ByteBuf) msg).toString(StandardCharsets.UTF_8);
            String mg = MsgRender.msg(ctx.channel().remoteAddress().toString(), clientMsg);
            channelGroup.writeAndFlush(Unpooled.copiedBuffer(mg.getBytes(StandardCharsets.UTF_8)));
//            channelGroup.forEach((channel) ->{
//                channel.writeAndFlush(Unpooled.copiedBuffer(mg.getBytes(StandardCharsets.UTF_8)));
//            });
//            ctx.writeAndFlush(Unpooled.copiedBuffer(mg.getBytes(StandardCharsets.UTF_8)));
            System.out.println(Thread.currentThread().getName() + mg);
            if ("close".equals(clientMsg)) {
                ctx.close();
            } else if ("stop server".equals(clientMsg)) {
                serverChannel.close();
            }

        }

    }


    public static void main(String[] args) {
        NettyServer nettyServer = new NettyServer();
        nettyServer.start();
    }

}
